#! /usr/bin/env python
#coding=utf-8

"""
    2011 Boris Tatarintsev
    
    Simple data cloud visualizer
    Represents an arry of any data as a cloud of tags
"""

import pygame
import math
import random
import os
import funclib
import __main__

from pygame.locals import *
from consts import *
from aabb import AABB
from odict import OrderedDict

class CloudVis:
    
    SCREEN_WIDTH = 800
    SCREEN_HEIGHT = 600
    
    SIZE_GRADATIONS = 10
    SIZE_MULT = 3
    BORDERS_PADDING = 5
    MAX_STR_LEN = 20
    
    
    def __init__(self, friendsData, windowWidth = SCREEN_WIDTH, windowHeight = SCREEN_HEIGHT):        
        self.m_windowWidth = windowWidth
        self.m_windowHeight = windowHeight
        self.m_rootPos = (self.m_windowWidth / 2, self.m_windowHeight / 2)
        self.m_isAlive = True        
        self.m_interests, self.m_maxGrad = funclib.fetchInterests(friendsData, 100)
        if self.m_maxGrad > CloudVis.SIZE_GRADATIONS:
            self.m_maxGrad = CloudVis.SIZE_GRADATIONS
        
    def __processEvents(self): 
        for event in pygame.event.get():
            if event.type == QUIT: 
                self.m_isAlive = False     
                __main__.createUI()
            elif event.type == KEYDOWN:
                #print event.key
                if(event.key == F5_KEY):       # F5 pressed rebuild tags cloud                    
                    self.__shuffleTags()
                if(event.key == LEFT_KEY):
                    if self.m_idx > 0:
                        self.m_idx -= 1
                if(event.key == RIGHT_KEY):
                    if self.m_idx < len(self.m_interests) - 1:
                        self.m_idx += 1
                
    def __prepareVisualData(self):
        # prepare data for fast visualization
        gradCount = self.m_maxGrad        
        # fonts sizes array
        # we assume that 0 indexed font is the biggest one        
        self.m_fonts = []
        for j in xrange(0, gradCount):
            s = int(CloudVis.SIZE_GRADATIONS * ((1. * gradCount - j) / gradCount) * CloudVis.SIZE_MULT)
            self.m_fonts.append(pygame.font.Font(self.m_fontPath, s))
        tmp_hash = OrderedDict()      
        block_size = len(self.m_interests) / gradCount
        block_idx = -1
        for idx in xrange(0, len(self.m_interests)):
            if idx % block_size == 0 and block_idx < gradCount - 1:
                block_idx += 1
            tmp_hash[self.m_interests[idx][0]] = block_idx        
        self.m_interests = tmp_hash
        
        # continue with spatial placement
    
        for item in self.m_interests.keys():
            block_idx = self.m_interests[item]            
            fnt = self.m_fonts[block_idx]            
            self.m_interests[item] = (AABB(), fnt)    
        
        self.__shuffleTags()
    
    def __shuffleTags(self):

        spatial_boxes = OrderedDict()
        screenArea = CloudVis.SCREEN_WIDTH * CloudVis.SCREEN_HEIGHT
        consumedArea = 0

        for item in self.m_interests.values():
            item[0].reset()
        
        # we will place our tags from largest to smallest
        # this will help to reduce sapce fragmentation

        for item in self.m_interests.keys():            
            fnt = self.m_interests[item][1]
            label = item
            if len(label) > CloudVis.MAX_STR_LEN: label = label[:CloudVis.MAX_STR_LEN] + '>'
            w, h = fnt.size(label)
            
            while True:
                
                x = random.randint(CloudVis.BORDERS_PADDING, CloudVis.SCREEN_WIDTH + 1 - w - CloudVis.BORDERS_PADDING)
                y = random.randint(CloudVis.BORDERS_PADDING, CloudVis.SCREEN_HEIGHT + 1 - h - CloudVis.BORDERS_PADDING)
                
                tmp_aabb = AABB(x, y, x + w, y + h)
                
                # check if we still have a free space on the screen
                if consumedArea + tmp_aabb.getArea() >= screenArea:
                    print "No enough free space!"
                    break
                    
                vals = spatial_boxes.values()
                intersection_flag = False
                
                for box in vals:                    
                    if funclib.AABBAABB_Intersect(box[0], tmp_aabb):
                        # boxes intersect, it is bad, we will need to generate new coords
                        intersection_flag = True
                        break
                        
                if not intersection_flag:
                    # box does not overlap, coool!
                    spatial_boxes[item] = (tmp_aabb, fnt)
                    consumedArea += tmp_aabb.getArea()                    
                    break
                    
        self.m_interests = spatial_boxes
        self.m_idx = 0
    
    def initPyGame(self):
        # initialize game window
        pygame.init()
        self.m_window = pygame.display.set_mode((self.m_windowWidth, self.m_windowHeight))
        self.m_screen = pygame.display.get_surface()
        # load images
        self.img = pygame.image.load(os.path.join("img", "clouds.png"))
        # init fonts
        self.m_fontPath = os.path.join("font", "xsuni.ttf")
        self.__prepareVisualData()
        
    def update(self):
        if not self.m_isAlive: return
        self.__processEvents()
    
    def paint(self):        
        if not self.m_isAlive: return
        self.m_screen.fill(WHITE_COLOR)
        for item in self.m_interests.keys():
            fnt = self.m_interests[item][1]
            x, y = self.m_interests[item][0].x1, self.m_interests[item][0].y1
            rgb = 255 - (255 * fnt.get_height()) / self.m_fonts[0].get_height()
            label = item
            if len(label) > CloudVis.MAX_STR_LEN: label = label[:CloudVis.MAX_STR_LEN] + '>'
            if self.m_interests.keys()[self.m_idx] == item:
                label = fnt.render(label, 1, (0, 0, 255))
            else:
                label = fnt.render(label, 1, (rgb, rgb, rgb))
            self.m_screen.blit(label, (x, y))
        self.m_screen.blit(self.img, (0, 0))
        #label = fnt.render("user1", 1, (0, 0, 0))
        #self.m_screen.blit(label, (0, 0))
        pygame.display.flip()
